package spitfire.ksim.simulation;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;

import spitfire.ksim.network.KNetAddress;
import spitfire.ksim.network.KNetwork;
import spitfire.ksim.network.KNetworkModule;
import spitfire.ksim.node.KGateway;
import spitfire.ksim.node.KNode;
import spitfire.ksim.node.KSensorNode;
import spitfire.ksim.simulator.KJob;
import spitfire.ksim.simulator.KSimulator;
import spitfire.ksim.util.TPoint;

public class KSimulation {

	private KSimulator simulator = new KSimulator();
	private KNetwork network = new KNetwork();
	private List<KNode> nodeList = new ArrayList<KNode>();
	
	public void startSimulation() {
		getSimulator().start();
	}
	
	public void startSimulationAfter(long interval) {
		getSimulator().startFrom(getSimulator().getFrontTime() + interval);
	}
	
	public void stopSimulation() {
		getSimulator().stop();
	}
	
	public void pauseSimulation() {
		getSimulator().pause();
	}
	
	public void resumeSimulation() {
		getSimulator().resume();
	}
	
	public void setTimeTick(long tick) {
		getSimulator().setTimeTick(tick);
	}
	
	public long getTimeTick() {
		return getSimulator().getTimeTick();
	}
	
	public void setRefreshPeriod(long period) {
		getSimulator().setRefreshPeriod(period);
	}
	
	public long getRefreshPeriod() {
		return getSimulator().getRefreshPeriod();
	}
	
	public boolean isStopped() {
		return getSimulator().isStopped();
	}
	
	public boolean isPaused() {
		return getSimulator().isPaused();
	}
	
	public KSensorNode addSensorNode(String dataPath, TPoint location) {
		KSensorNode sNode = new KSensorNode(getSimulator(), getNetwork(), location);
		nodeList.add(sNode);
		sNode.loadData(dataPath);
		simulator.coverDuration(sNode.getSimDuration());
		KJob updateJob = new KJob("6:0:0");
		updateJob.addProperty("type", "SD_UPDATE");
		// TODO modify the address
		updateJob.addProperty("target", KNetAddress.getAddr(0));
		sNode.addInitialJob(updateJob);
		KJob requestJob = new KJob("8:0:0");
		requestJob.addProperty("type", "SD_REQUEST");
		// TODO modify the address
		requestJob.addProperty("target", KNetAddress.getAddr(0));
		sNode.addInitialJob(requestJob);
		return sNode;
	}
	
	public KGateway addGateway(TPoint location) {
		KGateway gateway = new KGateway(getSimulator(), getNetwork(), location);
		gateway.setRequestAlgorithm("KNN-1:Representative");
		nodeList.add(gateway);
		return gateway;
	}
	
	public void removeNode(KNode node) {
		nodeList.remove(node);
		node.destroy();
	}

	/**
	 * @param scenarioPath
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public void saveScene(String scenarioPath)
			throws IOException {
		BufferedWriter writer = new BufferedWriter(new FileWriter(scenarioPath));
		PriorityQueue<KNetworkModule> modulePQueue = new PriorityQueue<KNetworkModule>(
				network.getNetModules().size(),
				new Comparator<KNetworkModule>() {
					@Override
					public int compare(KNetworkModule o1, KNetworkModule o2) {
						return o1.getNetAddr().compareTo(o2.getNetAddr());
					}
					
				});
		modulePQueue.addAll(network.getNetModules());
		for (KNetworkModule module : modulePQueue) {
			Object host = module.getHost();
			if (host instanceof KSensorNode) {
				KSensorNode sNode = (KSensorNode) host;
				writer.append("SensorNode;");
				writer.append(sNode.getDatapath() + ";");
				writer.append(sNode.getLocation().x + ";");
				writer.append(sNode.getLocation().y + ";");
				writer.append(sNode.getDescription() + "\n");
			} else if (host instanceof KGateway) {
				KGateway gateway = (KGateway) host;
				writer.append("Gateway;");
				writer.append(gateway.getLocation().x + ";");
				writer.append(gateway.getLocation().y + "\n");
			}
		}
		writer.close();
	}
	
	public KNode[] loadScene(String scenarioPath)
			throws IOException {
		List<KNode> nodeList = new ArrayList<KNode>();
		BufferedReader reader = new BufferedReader(new FileReader(scenarioPath));
		String line = null;
		while ((line = reader.readLine()) != null) {
			String[] splited = line.split(";");
			if (splited[0].equalsIgnoreCase("sensorNode")) {
				String dataPath = splited[1];
				double x = Double.valueOf(splited[2]);
				double y = Double.valueOf(splited[3]);
				KSensorNode s = addSensorNode(dataPath, new TPoint(x, y));
				if (splited.length < 5) {
					s.setDescription("");
				} else {
					s.setDescription(splited[4]);
				}
				nodeList.add(s);
			} else {
				double x = Double.valueOf(splited[1]);
				double y = Double.valueOf(splited[2]);
				KGateway gw = addGateway(new TPoint(x, y));
				nodeList.add(gw);
			}
		}
		reader.close();
		return nodeList.toArray(new KNode[nodeList.size()]);
	}
	
	public void setSimulator(KSimulator simulator) {
		this.simulator = simulator;
	}

	public KSimulator getSimulator() {
		return simulator;
	}

	public void setNetwork(KNetwork network) {
		this.network = network;
	}

	public KNetwork getNetwork() {
		return network;
	}
	
	public void setSimTime(long time) {
		simulator.setSimTime(time);
	}
	
	public long getSimTime() {
		return simulator.getSimTime();
	}

	public void destroy() {
		simulator.stop();
		for (KNode node : nodeList) {
			node.destroy();
		};
		nodeList.clear();
		simulator.destroy();
		network.destroy();
		KGateway.resetID();
	}
}
